package com.boot.aop;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.annotation.TableName;
import com.boot.app.system.domain.SystemLog;
import com.boot.app.system.service.ISystemLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

@Aspect
@Component
@Slf4j
public class LoggerAspect {

    private final ISystemLogService systemLogService;

    private LoggerAspect(ISystemLogService systemLogService){
        this.systemLogService = systemLogService;
    }


    /**
     * execution() 表达式的主体
     * com.boot.api.ApiResult 返回值类型， 可用*表示任意类型
     * com.boot aop所切的服务包名
     * .. 当前包及子包
     * * 第一个*表示所有类
     * .*(..) .*表示任何方法名，括号表示参数，两个点表示任意参数类型
     */
    @Pointcut("execution(com.boot.api.ApiResult com.boot.app..*.*(..))")
    public void controller(){}

    @Before("controller()")
    public void beforeController(JoinPoint joinPoint){
        log.info("==========Controller Request Before Start==========");
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = methodSignature.getMethod().getName();
        log.info("Request Method:{}", className + "." + methodName + "()");
        Arrays.stream(methodSignature.getParameterNames()).forEach(ele -> log.info("Request Parameter: {}", ele));
        Arrays.stream(joinPoint.getArgs()).forEach(ele -> log.info("Request Args: {}", ele));
        log.info("==========Controller Request Before End==========");
    }

    @Around("SystemLogger()")
    public Object aroundController(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("==========Controller Request Around Start==========");
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        SystemLogger annotation = signature.getMethod().getAnnotation(SystemLogger.class);
        SystemLog systemLog = new SystemLog();

        List<LoggerType> logTypes = Arrays.asList(annotation.type());
        boolean edit = new HashSet<>(logTypes).containsAll(getEditLogTypes());
        Long bizId;
        if(edit){
            bizId = JSONObject.from(proceedingJoinPoint.getArgs()[0]).getLong("id");
            if(bizId == null){
                systemLog.setType(LoggerType.INSERT.toString());
            }else{
                systemLog.setType(LoggerType.UPDATE.toString());
            }
        }
        Object[] args = proceedingJoinPoint.getArgs();
        systemLog.setObjectJson(JSONArray.toJSONString(args));//参数要在执行前赋值

        Object proceed = proceedingJoinPoint.proceed(args);//流程处理

        if(edit){
            bizId = JSONObject.from(proceedingJoinPoint.getArgs()[0]).getLong("id");
            systemLog.setBizId(bizId);
        }else if(logTypes.contains(LoggerType.DELETE)){
            systemLog.setType(LoggerType.DELETE.toString());
            systemLog.setBizId((Long) args[0]);
        }
        TableName tableName = (TableName)proceedingJoinPoint.getTarget().getClass().getAnnotation(SystemLogger.class).objectClass().getAnnotation(TableName.class);
        systemLog.setTableName(tableName.value());
        systemLogService.save(systemLog);
        log.info("==========Controller Request Around End==========");
        return proceed;
    }

    private ArrayList<LoggerType> getEditLogTypes(){
        return new ArrayList<LoggerType>(){{
            add(LoggerType.INSERT);
            add(LoggerType.UPDATE);
        }};
    }

    @Pointcut("@annotation(com.boot.aop.SystemLogger)")
    public void SystemLogger(){}

}
